package com.zmz.Genericity;

/**
 * Create with IntelliT IDEA
 *
 * @Author: zhengmingzhe
 * @Date: 2022/09/08/13:45
 * @Description: 普通泛型类 泛型有三种使用方式，分别为：泛型类、泛型接口、泛型方法
 * 通过泛型可以完成对一组类的操作对外开放相同的接口。最典型的就是各种容器类，如：List、Set、Map。
 */

//此处T可以随便写为任意标识，常见的如T、E、K、V等形式的参数常用于表示泛型
//在实例化泛型类时，必须指定T的具体类型
@SuppressWarnings("ALL")
public class Generic<T> {
    //key这个成员变量的类型为T,T的类型由外部指定
    private T key;

    public Generic(T key) { //构造方法形参key的类型也为T，T的类型由外部指定
        this.key = key;
    }

    public Generic() {
    }

    //我想说的其实是这个，虽然在方法中使用了泛型，但是这并不是一个泛型方法。
    //这只是类中一个普通的成员方法，只不过他的返回值是在声明泛型类已经声明过的泛型。
    //所以在这个方法中才可以继续使用 T 这个泛型 比如把这里的T替换成E就会报错
    public T getKey() { //返回值类型为T，T的类型由外部指定
        return key;
    }

    //在泛型类中声明了一个泛型方法，使用泛型E，这种泛型E可以为任意类型。可以类型与T相同，也可以不同。
    //由于泛型方法在声明的时候会声明泛型<E>，因此即使在泛型类中并未声明泛型，编译器也能够正确识别泛型方法中识别的泛型。
    @SuppressWarnings("AlibabaCommentsMustBeJavadocFormat")
    public <E> void show(E t) {
        System.out.println(t.toString());
    }

    public void showKeyValue(Generic<?> obj) {
        System.out.println("泛型通配符测试: key value is " + obj.getKey());
    }

    /**
     * 泛型方法的基本介绍
     *
     * @param tClass 传入的泛型实参
     * @return T 返回值为T类型
     * 说明：
     * 1）public 与 返回值中间<T>非常重要，可以理解为声明此方法为泛型方法。
     * 2）只有声明了<T>的方法才是泛型方法，泛型类中的使用了泛型的成员方法并不是泛型方法。
     * 3）<T>表明该方法将使用泛型类型T，此时才可以在方法中使用泛型类型T。
     * 4）与泛型类的定义一样，此处T可以随便写为任意标识，常见的如T、E、K、V等形式的参数常用于表示泛型。
     */
    public <T> T genericMethod(Class<T> tClass) throws InstantiationException,
            IllegalAccessException {
        T instance = tClass.newInstance();
        return instance;
    }

    /*
     * @Description  泛型方法可变参数
     * @Param
     * @return
     **/
    public <T> void printMsg(T... args) {
        for (T t : args) {
            System.out.println("泛型可变参数测试:t is " + t);
        }
    }

    /**
     * 静态方法无法访问类上定义的泛型，需要添加额外的泛型声明（将这个方法定义成泛型方法）
     * 即使静态方法要使用泛型类中已经声明过的泛型也不可以。
     * 如：public static void show(T t){..},此时编译器会提示错误信息：
     * "StaticGenerator cannot be refrenced from static context"
     */
    public static <T> void show2(T t) {

    }

    /*
     * @Description 为泛型添加上边界，即传入的类型实参必须是指定类型的子类型
     * <? extends E> extends 关键字声明了类型的上界，表示参数化的类型可能是所指定的类型，或者是此类型的子类
       <? super E> super 关键字声明了类型的下界，表示参数化的类型可能是指定的类型，或者是此类型的父类
     * @Param
     * @return
     **/
    public void showKeyValue1(Generic<? extends Number> obj) {
        System.out.println("泛型上边界测试:key value is " + obj.getKey());
    }

    /*
     * @Description  这个也是上边界问题 返回值只能是所指定类型的子类型
     * @Param
     * @return
     **/
    public Generic<? extends Number> showKeyValue2() {
//        下面这一行注释掉的就会报错
//        Generic<String> a = new Generic<>();
        Generic<Integer> a = new Generic<>();
        return a;
    }
}
